#include "CLayer.h"
#include "CCore.h"
#include "CVideoManager.h"
#include "CScene.h"
#include <algorithm>
CLayer::CLayer()
{
    cleanTime.Init(5000);
    Speed.fx = Speed.fy = 1.0f;
    pr_Scene = NULL;
    p_Bricks = NULL;
    bVisible = true;
}

CLayer::~CLayer()
{
    Release();
}

bool CLayer::Release()
{
    int i;
    for (i = (int)Sprites.size()-1; i >= 0; i--){
        Sprites[i]->Release();
        delete Sprites[i];
    }
    Sprites.clear();
    ClearBricks();
    return true;
}

bool CLayer::DeleteSprite(CSprite **p_Sprite)
{
    if (p_Sprite && *p_Sprite){
        for (int i = (int)Sprites.size()-1; i >=0; i--){
            if (Sprites[i] == *p_Sprite){
                Sprites[i]->Release();
                delete Sprites[i];
                *p_Sprite = NULL;
                Sprites.erase(Sprites.begin()+i);
                return true;
            }
        }
    }
    return false;
}


void CLayer::Update()
{
    CVector LayerSize = GetLayerSize();
    if (LayerSize.fx >= CVideoManager::getInstance()->WindowSize.fx && LayerSize.fy >= CVideoManager::getInstance()->WindowSize.fy){
        for (int i = (int)Sprites.size()-1; i >=0; i-- ){
            Sprites[i]->Update();
            Sprites[i]->Execute();


            Sprites[i]->Position.fx = (Sprites[i]->Position.fx < 0)?
                                      Sprites[i]->Position.fx + LayerSize.fx :
                                      Sprites[i]->Position.fx;
            Sprites[i]->Position.fy = (Sprites[i]->Position.fy < 0)?
                                      Sprites[i]->Position.fy + LayerSize.fy :
                                      Sprites[i]->Position.fy;

            Sprites[i]->Position.fx = (Sprites[i]->Position.fx > LayerSize.fx)?
                                      Sprites[i]->Position.fx - LayerSize.fx :
                                      Sprites[i]->Position.fx;
            Sprites[i]->Position.fy = (Sprites[i]->Position.fy > LayerSize.fy)?
                                      Sprites[i]->Position.fy - LayerSize.fy :
                                      Sprites[i]->Position.fy;
        }
    }else{
        for (int i = (int)Sprites.size()-1; i >=0; i-- ){
            Sprites[i]->Update();
            Sprites[i]->Execute();
        }
    }

    cleanTime.Update();
    if(cleanTime.Ended()){
        CleanUp();
        cleanTime.Restart();
    }
}

void CLayer::Draw()
{
    if (p_Bricks){
        int i = 0,j = 0;

        CVector Pos = Offset;

        Pos.Floor();
        while (Pos.fx > 0){
            Pos.fx -= BrickSize.fx;
            i--;
            i = (i < 0)? ((int)NumBricks.fx) - 1 : i;
        }

        while (Pos.fy > 0){
            Pos.fy -= BrickSize.fy;
            j--;
            j = (j < 0)? ((int)NumBricks.fy) - 1 : j;
        }

        float fXPos = Pos.fx;
        int iStarti = i;
        int iSize = (int)NumBricks.fx;
        int jSize = (int)NumBricks.fy;

        for (; Pos.fy < (int)CVideoManager::getInstance()->WindowSize.fy; j = (j+1)%jSize){
            for (; Pos.fx < (int)CVideoManager::getInstance()->WindowSize.fx; i = (i+1)%iSize){
                if ( p_Bricks[i + (j*iSize)] ){
                    CVideoManager::getInstance()->Screen.DrawImage(
                        Pos,
                        pr_Scene->TileImages[p_Bricks[i + (j*iSize)]->iImage],
                        p_Bricks[i + (j*iSize)]->iFrame
                    );
                }
                Pos.fx += BrickSize.fx;
            }
            Pos.fy += BrickSize.fy;
            i = iStarti;
            Pos.fx = fXPos;
        }
    }

    int iSprites = (int)Sprites.size();
    for (int i = 0; i < iSprites; i++){
        if (Sprites[i]->bVisible)
            Sprites[i]->Draw();
    }
}

CSprite* CLayer::CreateSprite(const std::string& cp_chFileName,int h, int w)
{
    return CreateSprite(cp_chFileName,CVector(w,h));
}

CSprite* CLayer::CreateSprite(const std::string& cp_chFileName,const CVector& cFrameSize)
{
    CSprite *p_Sprite = new CSprite();
    if (p_Sprite){
        p_Sprite->pr_Layer = this;
        p_Sprite->Create(cp_chFileName,cFrameSize);
        Sprites.push_back(p_Sprite);
        return p_Sprite;
    }
    return NULL;
}

CSprite* CLayer::AddSprite(CSprite* p_Sprite)
{
    if(p_Sprite){
        p_Sprite->pr_Layer = this;
        Sprites.push_back(p_Sprite);
        return p_Sprite;
    }

    return NULL;
}

void CLayer::Scroll(const CVector& cScroll)
{
    Offset.fx += cScroll.fx*Speed.fx;
    Offset.fy += cScroll.fy*Speed.fy;

    CVector LayerSize = GetLayerSize();

    if (LayerSize.fx > 0.0f){
        while (Offset.fx < 0.0f)
            Offset.fx += LayerSize.fx;
        while (Offset.fx > LayerSize.fx)
            Offset.fx -= LayerSize.fx;
    }

    if (LayerSize.fy > 0.0f){
        while (Offset.fy < 0.0f)
            Offset.fy += LayerSize.fy;
        while (Offset.fy > LayerSize.fy)
            Offset.fy -= LayerSize.fy;
    }
}

void CLayer::CreateBricks(const CVector &cNumBricks)
{
    ClearBricks();
    NumBricks = cNumBricks;
    NumBricks.Floor();

    int iSize = (int)NumBricks.fx * (int)NumBricks.fy;
    p_Bricks = new CBrick*[iSize];

    memset(p_Bricks,0,iSize*sizeof(CBrick*));
}

void CLayer::ClearBricks()
{
    if (p_Bricks){
        int iSize = (int)NumBricks.fx*(int)NumBricks.fy;
        for (int i = 0; i < iSize; i++){
            if (p_Bricks[i]){
                delete p_Bricks[i];
                p_Bricks[i] = NULL;
            }
        }
        delete [] p_Bricks;
        p_Bricks = NULL;
    }
}

void CLayer::SetBrick(const CVector& cBrickPos, int iImage, int iFrame)
{
    if (p_Bricks &&
            cBrickPos.fy >= 0 &&
            cBrickPos.fy < NumBricks.fy &&
            cBrickPos.fx >= 0 &&
            cBrickPos.fx < NumBricks.fx
       ){
        int iPos = ((int)cBrickPos.fx) + ((int)cBrickPos.fy * (int)NumBricks.fx);

        if (!p_Bricks[iPos])
            p_Bricks[iPos]= new CBrick();

        p_Bricks[iPos]->iFrame = iFrame;
        p_Bricks[iPos]->iImage = iImage;
    }
}

void CLayer::CreateBricksFromBitmap(const std::string& cp_chFileName, stBrickConfig* pr_BrickConfigs, int iConfigCount)
{
    CImage *mapImage = new CImage();
    mapImage = CVideoManager::getInstance()->LoadImage(cp_chFileName);
    if (mapImage){
        SDL_Color ColorList;
        unsigned int uiRGB,uiRGBPixel;
        int i = 0;
        int j = 0;
        int n = 0;

        CreateBricks(CVector((float) mapImage->Size.fx,(float) mapImage->Size.fy));

        SDL_LockSurface(mapImage->p_Surface);
        for (i = 0;i <= (int) mapImage->Size.fx;i++){
            for (j = 0;j <= (int) mapImage->Size.fy;j++){

                ColorList  = mapImage->GetPixel(i,j);
                uiRGBPixel = SDL_MapRGB(mapImage->p_Surface->format,ColorList.r,ColorList.g,ColorList.b);
                for (n = 0;n <= iConfigCount;n++){
                    uiRGB = pr_BrickConfigs[n].uiColor;
                    if (uiRGB == uiRGBPixel)
                         SetBrick(CVector((float)i,(float)j),pr_BrickConfigs[n].iImage,pr_BrickConfigs[n].iFrame);
                 }
            }
        }
        SDL_UnlockSurface(mapImage->p_Surface);
    }

    mapImage->Release();
}


CVector CLayer::GetLayerSize()
{
    return CVector(NumBricks.fx*BrickSize.fx, NumBricks.fy*BrickSize.fy);
}

CVector CLayer::GetLayerPos(const CVector& BkBuffPos)
{
    CVector LayerSize = GetLayerSize();
    CVector PosRet = ((LayerSize - Offset) + BkBuffPos);
    while (PosRet.fx < 0.0f)
        PosRet.fx += LayerSize.fx;
    while (PosRet.fy < 0.0f)
        PosRet.fy += LayerSize.fy;
    while (PosRet.fx > LayerSize.fx)
        PosRet.fx -= LayerSize.fx;
    while (PosRet.fy > LayerSize.fy)
        PosRet.fy -= LayerSize.fy;
    return PosRet;
}

CVector CLayer::GetBackBufferPos(const CVector& LayerPos, const CVector& FrameSize)
{

    CVector LayerSize = GetLayerSize();
    CVector PosRet = LayerPos;
    if (LayerSize.fx >= CVideoManager::getInstance()->WindowSize.fx && LayerSize.fy >= CVideoManager::getInstance()->WindowSize.fy){
        CVector Aux = (LayerSize - Offset);
        Aux.fx = -Aux.fx;
        Aux.fy = -Aux.fy;
        while (PosRet.fx < -FrameSize.fx)
            PosRet.fx += LayerSize.fx;
        while (PosRet.fy < -FrameSize.fy)
            PosRet.fy += LayerSize.fy;
        while (PosRet.fx > LayerSize.fx)
            PosRet.fx -= LayerSize.fx;
        while (PosRet.fy > LayerSize.fy)
            PosRet.fy -= LayerSize.fy;
    }
    return PosRet;
}

bool CanDelete(CSprite* spt)
{
    return spt->bCanDelete;
}

void CLayer::CleanUp()
{
    try
    {
        Sprites.erase(std::remove_if(Sprites.begin(),Sprites.end(),CanDelete),Sprites.end());
    }catch(std::exception& e){
        std::cout << "exception " << e.what() << std::endl;
    }
}
